#include "CMSInterface.h"
#include "Include.h"
#include "Settings.h"

#ifdef ACE_WIN32
#define WIN_TEST
#endif

std::map<std::string , CMS_OBJECT_DATA> CMS_OBJECT_DATA::gCMSObjectMap;
ACE_Thread_Mutex	CMS_OBJECT_DATA::gCMSObjectLock;

std::vector<std::string> CMS_OBJECT_DATA::GetToGetObjectList(std::set<std::string>& objIdSet)
{
	std::vector<std::string> toGetList;

	gCMSObjectLock.acquire();

	std::set<std::string>::iterator iter;
	for(iter = objIdSet.begin() ; iter != objIdSet.end() ; iter++)
	{
		if(gCMSObjectMap.find(*iter) == gCMSObjectMap.end())
			toGetList.push_back(*iter);
	}

	gCMSObjectLock.release();

	return toGetList;
}

bool CMS_OBJECT_DATA::GetObjectData(const std::string& objId , CMS_OBJECT_DATA& data)
{
	std::map<std::string,CMS_OBJECT_DATA>::iterator it;
	bool find = false;

	gCMSObjectLock.acquire();

	it = gCMSObjectMap.find(objId);
	if( it != gCMSObjectMap.end())
	{
		data = it->second;
		find = true;
	}

	gCMSObjectLock.release();

	return find;
}

std::string CMS_OBJECT_DATA::GetBGMPath(const std::string& bgmId)
{
	std::map<std::string,CMS_OBJECT_DATA>::iterator it;
	std::string bgm_path = "";

	gCMSObjectLock.acquire();

	it = gCMSObjectMap.find(bgmId);
	if( it != gCMSObjectMap.end())
	{
		bgm_path = Settings::GetSettings()->OBJECT_DIR + (it->second).sound;
		
	}

	gCMSObjectLock.release();


	return bgm_path;
}


int CMS_OBJECT_DATA::PutObjectList(const Json::Value& v)
{
	std::string objId;
	int count = 0;

	gCMSObjectLock.acquire();

	for(int i = 0 ; i < v.size() ; i ++)
	{
		objId = PACKET_BASE::GetStringS(v[i] , "objectId");
		if(objId.length() > 0)
		{
			if(gCMSObjectMap.find(objId) == gCMSObjectMap.end())
			{
				gCMSObjectMap[objId] = CMS_OBJECT_DATA(objId , v[i]);
				printf("Add CMS Object List : %s\n" , objId.c_str());
			}

			count ++;
		}

	}

	gCMSObjectLock.release();

	return count;
}



bool CMSInterface::Connect(ACE_SOCK_Stream& stream)
{
	ACE_INET_Addr iaddr(Settings::GetSettings()->CMS_PORT , Settings::GetSettings()->CONTROLLER_ADDR.c_str());

	ACE_SOCK_Connector  connector;
	ACE_Time_Value timeout(10); 
	if(connector.connect(stream , iaddr , &timeout) == -1)
	{
		LOG::PushProcessLog(_ERROR , std::string("CMS connection error : ") + Settings::GetSettings()->CONTROLLER_ADDR);
		printf("CMS connection error=> %s:%d\n" ,Settings::GetSettings()->CONTROLLER_ADDR.c_str() , Settings::GetSettings()->CMS_PORT );
		return false;
	}

	return true;
}

bool CMSInterface::Disconnect(ACE_SOCK_Stream & stream)
{
	return stream.close();
}

std::string CMSInterface::RemoveChunkSize(std::string& body)
{
	const std::string find_str = "\r\n";

	std::string ret;

	int start_pos = 0;

	while(true)
	{

		int first_pos = body.find(find_str , start_pos);
		if(-1 == first_pos)
			break;

		int second_pos = body.find(find_str ,first_pos+find_str.length());
		if(-1 == second_pos)
			break;

		ret += body.substr(start_pos , first_pos - start_pos);

		start_pos = second_pos + find_str.length();


	}

	ret += body.substr(start_pos);

	return ret;
}


std::string CMSInterface::Snd2CMS(const std::string& req)
{
	ACE_SOCK_Stream stream;

	if(!Connect(stream))
		return "";

	if(stream.send_n(req.c_str() , req.length()) <= 0)
	{
		LOG::PushProcessLog(_ERROR , "CMS request error\n");
		printf("CMS request error\n");
		return "";
	}

	char	buf[MAX_BUFFER + 1];
	memset(buf , 0x00 , MAX_BUFFER + 1);
	int resp_size = 0;
	std::string resp;

	while( (resp_size = stream.recv(buf , MAX_BUFFER)) > 0)
	{
		resp += buf;
		memset(buf , 0x00 , MAX_BUFFER + 1);
	}

	Disconnect(stream);


	int a = resp.find("{");
	int b = resp.rfind("}");

	if( a >= 0 && b >= 0 && a < b)
	{
		std::string json_body = resp.substr(a , b-a+1); 
		return RemoveChunkSize(json_body);
	}

	return "";

}


std::string CMSInterface::GetCMSJsonString(const std::string& addr)
{
	std::string req = std::string("GET /") + Settings::GetSettings()->CMS_VERSION + addr + " HTTP/1.1\r\n";
	req += std::string("Host: ") + Settings::GetSettings()->CONTROLLER_ADDR +"\r\n";
	req += "Accept: application/json\r\n";
	req += "Cache-Control: no-cache\r\n";
	req += "Connection: close\r\n";		
	req += "\r\n";

	return Snd2CMS(req);

}

std::string CMSInterface::PostCMSJsonString(const std::string& addr , PACKET_BASE& content)
{
	std::string req = std::string("POST /") + Settings::GetSettings()->CMS_VERSION + addr + " HTTP/1.1\r\n";
	req += std::string("Host: ") + Settings::GetSettings()->CONTROLLER_ADDR +"\r\n";
	req += "Accept: application/json\r\n";
	req += "Cache-Control: no-cache\r\n";
	req += "Connection: close\r\n";		
	req += "Content-Type: application/json\r\n";

	char buf[MAX_BUFFER];
	sprintf(buf , "%d" , content.ToString().length());
	req += std::string("Content-Length: ") + buf + "\r\n";

	req += "\r\n";
	req += content.ToString();

	return Snd2CMS(req);

}

std::string CMSInterface::PostCMSJsonString(const std::string& addr , const std::string& name , const std::string& value)
{
	std::string req = std::string("POST /") + Settings::GetSettings()->CMS_VERSION + addr + " HTTP/1.1\r\n";
	req += std::string("Host: ") + Settings::GetSettings()->CONTROLLER_ADDR +"\r\n";
	req += "Accept: application/json\r\n";
	req += "Cache-Control: no-cache\r\n";
	req += "Connection: close\r\n";		
	req += "Content-Type: application/x-www-form-urlencoded\r\n";

	std::string data = name + "=" + value;

	char buf[MAX_BUFFER];
	sprintf(buf , "%d" , data.length());
	req += std::string("Content-Length: ") + buf + "\r\n";

	req += "\r\n";
	req += data;

	return Snd2CMS(req);

}

bool CMSInterface::Succeeded(const std::string& json_str)
{
	bool ret = false;
	if(json_str.length() > 0)
	{
		Json::Value root;
	
		if(PACKET_BASE::GetRoot(json_str , root) && PACKET_BASE::GetStringS(root , "code") == "200")
		{
			ret = true;
		}
	}

	return ret;

}


int CMSInterface::GetJobList(int max_size , std::vector<_JOB_INFO>& v)
{
	v.clear();
	
	char	buf[100];
	memset(buf , 0x00 , 100);
	sprintf(buf , "%d" , max_size);

	PACKET_TMP content;
	content.SetParam("rendererId" , Settings::GetSettings()->RENDERER_ID);
	content.SetParam("count" , buf);

	time_t start = time(NULL);

	std::string json_str = PostCMSJsonString("/renderer/getWaitingJobList/" , content);

	if(json_str.length() > 0)
	{

		Json::Value root;
		if(PACKET_BASE::GetRoot(json_str , root))
		{
			if(PACKET_BASE::GetStringS(root , "code") == "200")
			{
				PACKET_JOBLIST pjl(root);

				if(pjl.GetSize() > 0)
				{


					for(int i = 0 ; i < pjl.GetSize() ; i ++)
					{
						if(pjl.GetJobId(i).length() > 0)
						{

							_JOB_INFO ji = pjl.GetJobInfo(i);

							v.push_back(ji);
						}
					}
				}
			}
		}
	}

	if(v.size() > 0)
		printf("[cms] get job list[%d] %ds\n" , v.size() , time(NULL) - start);

	return v.size();
}

bool CMSInterface::CancelAll()
{

	PACKET_TMP content;
	content.SetParam("rendererId" , Settings::GetSettings()->RENDERER_ID);

	std::string json_str = PostCMSJsonString("/renderer/setAll2Waiting/" , content);
	bool ret = Succeeded(json_str);

	if(ret)
	{
		LOG::PushProcessLog(_INFO  , "[cms] cancelAll");
	}
	else
	{
		LOG::PushProcessLog(_ERROR , "[cms] cancelAll");
		printf("[cms] failed to cancelAll\n");
	}


	return ret;
}

bool CMSInterface::SucceededJob(ReservedInfo& info)
{
	Json::Value root;

	time_t start = time(NULL);

	root["rendererId"] = Settings::GetSettings()->RENDERER_ID;
	root["jobId"] = info.GetJobId();
	root["resultPath"] = info.job_info.GetResultPath();

	char tmp[10];
	sprintf(tmp , "%.2f" , info.job_info.GetDuration());
	root["length"] = tmp;

	root["width"] = info.job_info.GetHighResolution().GetWidth();
	root["height"] = info.job_info.GetHighResolution().GetHeight(); 

	PACKET_TMP content;
	content.SetRoot(root);

	std::string json_str = PostCMSJsonString("/renderer/succeededJob/" , content);

	bool ret = Succeeded(json_str);

	int elapsed = time(NULL) - start;

	if(ret)
	{
		LOG::PushJobLog(_INFO , info.GetJobId() , "[cms] succeededJob ended");
		printf("[cms] succeededJob ended[%s] %ds\n" , info.GetJobId().c_str() , elapsed);
	}
	else
	{
		LOG::PushJobLog(_REQUIRED , info.GetJobId() , "[cms] succeededJob failed"); 
		printf("[cms] succeededJob failed [%s] %ds => retry \n" , info.GetJobId().c_str()  , elapsed);
	}

	return ret;
}

bool CMSInterface::FailedJob(ReservedInfo& info)
{
	Json::Value root;

	time_t start = time(NULL);


	root["rendererId"] = Settings::GetSettings()->RENDERER_ID;
	root["jobId"] = info.GetJobId();

	PACKET_TMP content;
	content.SetRoot(root);

	std::string json_str = PostCMSJsonString("/renderer/failedJob/" , content);

	bool ret = Succeeded(json_str);

	int elapsed = time(NULL) - start;

	if(ret)
	{
		LOG::PushJobLog(_INFO , info.GetJobId() , "[cms] failedJob ended");
		printf("[cms] failedJob ended[%s] %ds\n" , info.GetJobId().c_str() , elapsed);

	}
	else
	{
		LOG::PushJobLog(_ERROR , info.GetJobId() , "[cms] failedJob failed");
		printf("[cms] failedJob failed [%s] %ds\n" , info.GetJobId().c_str() , elapsed);

	}

	return ret;
}


bool CMSInterface::GetObjectInfoList(std::set<std::string> &setObjectId)
{
	std::vector<std::string> toGetList = CMS_OBJECT_DATA::GetToGetObjectList(setObjectId);
	if(toGetList.size() == 0)
		return true;


	std::string strObjectIdList = "";

	for(int i = 0 ; i < toGetList.size() ; i ++)
	{
		if(strObjectIdList.length() > 0)
			strObjectIdList += ",";
		strObjectIdList += toGetList[i];
	}

	PACKET_TMP content;
	content.SetParam("objectIdList" , strObjectIdList);

	std::string json_str = PostCMSJsonString("/renderer/getObjectFileList" , content);

	bool ret = false;

	if(json_str.length() > 0)
	{
		Json::Value root;

		if(PACKET_BASE::GetRoot(json_str , root))
		{
			if(PACKET_BASE::GetStringS(root , "code") == "200")
			{
				if(CMS_OBJECT_DATA::PutObjectList(root["body"]) > 0)
					ret = true;
			}
		}
	}

	return ret;
}


CMS_EFFECT_DATA CMSInterface::GetEffectData(const std::string& effect_id)
{
	CMS_OBJECT_DATA obj_data;
	if(!CMS_OBJECT_DATA::GetObjectData(effect_id , obj_data))
	{
		return CMS_EFFECT_DATA(); 
	}

	return CMS_EFFECT_DATA(obj_data.video.length() > 0 ? obj_data.video : obj_data.image , obj_data.sound);
}

CMS_FILTER_DATA CMSInterface::GetFilterData(const std::string& filter_id)
{
	CMS_OBJECT_DATA obj_data;
	if(!CMS_OBJECT_DATA::GetObjectData(filter_id , obj_data))
	{
		return CMS_FILTER_DATA(); 
	}

	return CMS_FILTER_DATA(obj_data.video.length() > 0 ? obj_data.video : obj_data.image
						, obj_data.blending == "none" ? "" : obj_data.blending 
						, obj_data.opacity.length() == 0 ? 0 : atof(obj_data.opacity.c_str()) / 10.0 , obj_data.curve);

}

#ifdef _TEST_
int test_count = 0;
#endif



int CMSInterface::GetWaitingJobCount()
{

	PACKET_TMP content;
	content.SetParam("rendererId" ,Settings::GetSettings()->RENDERER_ID);

	std::string json_str = PostCMSJsonString("/renderer/getWaitingJobCount" , content);


	int count = -1;
	if(json_str.length() > 0)
	{

		Json::Value root;
	
		if(PACKET_BASE::GetRoot(json_str , root))
		{
			if(PACKET_BASE::GetStringS(root , "code") == "200")
			{
				count = PACKET_BASE::GetIntS(root ,"body" , "waitingCount") + PACKET_BASE::GetIntS(root ,"body" , "readyCount");
			}
			
		}
	}

	return count;
}


bool load_test_start = false;
bool CMSInterface::RegistFiles()
{
	if(!load_test_start)
	{
		srand(time(NULL));
		load_test_start = true;
	}

	time_t begin = time(NULL);



	std::string videoId;
	std::string scriptId;
	std::string path;
	std::string s3Url;
	std::string sessionId = "7365f9b0c25f11e48faa022a215b83d6";

	std::string json_str = PostCMSJsonString("/renderer/prepareArticleTest" , "sessionId" , sessionId );

	bool ret = false;
	if(json_str.length() > 0)
	{
		Json::Value root;

		if(PACKET_BASE::GetRoot(json_str , root))
		{
		
			if(PACKET_BASE::GetStringS(root , "code") == "202")
			{
				scriptId	= PACKET_BASE::GetStringS(root , "body" , "scriptFileId");
				videoId		= PACKET_BASE::GetStringS(root , "body" , "videoFileId");
				s3Url		= PACKET_BASE::GetStringS(root , "body" , "s3Url");
				path		=  PACKET_BASE::GetStringS(root , "body" , "path");
				
				ret = true;
			}
		
		}
	}

	if(!ret)
	{
		printf("parepareFileUploadTest API failed\n");
		LOG::PushProcessLog(_ERROR , "parepareFileUploadTest API failed");
		return false;
	}

	int file_index = rand() % 4 + 1;

	char cmd[1024];

	sprintf(cmd , "aws s3 cp s3://alive-loadtest/samples/%d.mov %s.mov" , file_index , (s3Url + "/" + path.c_str() + videoId).c_str() ); //ios
	if(_SUCCESS != PROCESS::Execute(_EXE_FILE , cmd))
	{
		printf("%s failed\n" , cmd);
		LOG::PushProcessLog(_ERROR , cmd);
		return false;
	}

	sprintf(cmd , "aws s3 cp s3://alive-loadtest/samples/%d.json %s.json" , file_index , (s3Url + "/" + path.c_str() + scriptId).c_str() );
	if(_SUCCESS != PROCESS::Execute(_EXE_FILE , cmd))
	{
		printf("%s failed\n" , cmd);
		LOG::PushProcessLog(_ERROR , cmd);
		return false;
	}

	PACKET_TMP content;
	content.SetParam("sessionId" , sessionId);
	content.SetParam("path" , path);
	content.SetParam("videoFileId" , videoId);
	content.SetParam("videoFileExt" , "mov"); 
	content.SetParam("scriptFileId" , scriptId);
	content.SetParam("scriptFileExt" , "json");
	content.SetParam("articleState" , "1");

	json_str = PostCMSJsonString("/article/registerArticle" , content);

	ret = false;
	if(json_str.length() > 0)
	{
		Json::Value root;

		if(PACKET_BASE::GetRoot(json_str , root))
		{
			if(PACKET_BASE::GetStringS(root , "code") == "202")
			{
				ret = true;
			}
		}
	}
	if(!ret)
	{
		printf("registPostTest API failed\n");
		LOG::PushProcessLog(_ERROR , "registPostTest API failed");
		return false;
	}

	time_t end = time(NULL);
	printf("regist artible elapsed : %ds \n",(int)(end-begin) );

	return true;

}